package http

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// CachedUpload 缓存的上传请求
type CachedUpload struct {
	RequestID     string
	TunnelID      string
	Method        string // POST/PUT
	Path          string // /test/upload
	Headers       map[string]string
	Body          []byte // 小文件：内存缓存
	FilePath      string // 大文件：磁盘路径
	ContentType   string
	ContentLength int64 // 原始Content-Length
	IsChunked     bool  // 是否为chunked编码
	IsLargeFile   bool  // 是否为大文件（磁盘存储）
	ChunkSize     int64 // 分片大小（大文件模式）
	TotalChunks   int   // 总分片数（大文件模式）
	CreatedAt     time.Time
	ExpiresAt     time.Time
}

// UploadCacheManager 上传缓存管理器
type UploadCacheManager struct {
	cache         sync.Map      // requestID -> *CachedUpload
	maxMemorySize int64         // 内存缓存阈值(默认10MB)
	maxFileSize   int64         // 最大文件大小(默认10GB)
	chunkSize     int64         // 分片大小(默认5MB)
	storageDir    string        // 磁盘存储目录
	ttl           time.Duration // 缓存过期时间(默认30分钟)
	mu            sync.RWMutex
}

// NewUploadCacheManager 创建上传缓存管理器
func NewUploadCacheManager() *UploadCacheManager {
	storageDir := "./upload_cache"
	if err := os.MkdirAll(storageDir, 0755); err != nil {
		log.Printf("⚠️  [上传缓存] 创建存储目录失败: %v", err)
	}

	manager := &UploadCacheManager{
		maxMemorySize: 10 * 1024 * 1024,        // 10MB内存阈值
		maxFileSize:   10 * 1024 * 1024 * 1024, // 10GB最大文件
		chunkSize:     5 * 1024 * 1024,         // 5MB分片
		storageDir:    storageDir,
		ttl:           30 * time.Minute, // 30分钟过期
	}

	// 启动定时清理
	go manager.cleanupLoop()

	log.Printf("✅ [上传缓存] 管理器已启动")
	log.Printf("   ├─ 内存阈值: %.2f MB", float64(manager.maxMemorySize)/(1024*1024))
	log.Printf("   ├─ 最大文件: %.2f GB", float64(manager.maxFileSize)/(1024*1024*1024))
	log.Printf("   ├─ 分片大小: %.2f MB", float64(manager.chunkSize)/(1024*1024))
	log.Printf("   ├─ 存储目录: %s", storageDir)
	log.Printf("   └─ 过期时间: %s", manager.ttl)

	return manager
}

// Cache 缓存上传请求（智能选择内存或磁盘）
func (m *UploadCacheManager) Cache(requestID string, upload *CachedUpload) error {
	bodySize := int64(len(upload.Body))

	// 检查最大文件限制
	if bodySize > m.maxFileSize {
		return fmt.Errorf("文件过大: %.2f GB, 最大: %.2f GB",
			float64(bodySize)/(1024*1024*1024),
			float64(m.maxFileSize)/(1024*1024*1024))
	}

	// 设置过期时间
	upload.CreatedAt = time.Now()
	upload.ExpiresAt = time.Now().Add(m.ttl)

	// 🔥 智能选择存储方式
	if bodySize > m.maxMemorySize {
		// 大文件：保存到磁盘并分片
		log.Printf("💾 [上传缓存] 大文件检测: %.2f MB，使用磁盘存储", float64(bodySize)/(1024*1024))
		if err := m.saveToDisk(requestID, upload); err != nil {
			return fmt.Errorf("保存到磁盘失败: %v", err)
		}
	} else {
		// 小文件：保存到内存
		upload.IsLargeFile = false
		log.Printf("💾 [上传缓存] 小文件: %.2f KB，使用内存存储", float64(bodySize)/1024)
	}

	// 缓存元数据
	m.cache.Store(requestID, upload)

	chunkedTag := ""
	if upload.IsChunked {
		chunkedTag = " [CHUNKED]"
	}

	log.Printf("✅ [上传缓存] 已缓存: RequestID=%s, Size=%.2f MB%s, 模式=%s, 过期=%s",
		requestID[:8]+"...",
		float64(bodySize)/(1024*1024),
		chunkedTag,
		map[bool]string{true: "磁盘", false: "内存"}[upload.IsLargeFile],
		upload.ExpiresAt.Format("15:04:05"))

	return nil
}

// saveToDisk 保存大文件到磁盘并分片
func (m *UploadCacheManager) saveToDisk(requestID string, upload *CachedUpload) error {
	// 创建请求专用目录
	requestDir := filepath.Join(m.storageDir, requestID)
	if err := os.MkdirAll(requestDir, 0755); err != nil {
		return fmt.Errorf("创建目录失败: %v", err)
	}

	// 计算分片信息
	bodySize := int64(len(upload.Body))
	upload.ChunkSize = m.chunkSize
	upload.TotalChunks = int((bodySize + m.chunkSize - 1) / m.chunkSize)
	upload.IsLargeFile = true

	log.Printf("📦 [磁盘存储] 开始分片: 总大小=%.2f MB, 分片数=%d",
		float64(bodySize)/(1024*1024), upload.TotalChunks)

	// 分片保存
	for i := 0; i < upload.TotalChunks; i++ {
		start := int64(i) * m.chunkSize
		end := start + m.chunkSize
		if end > bodySize {
			end = bodySize
		}

		chunkData := upload.Body[start:end]
		chunkPath := filepath.Join(requestDir, fmt.Sprintf("chunk_%d.dat", i))

		if err := os.WriteFile(chunkPath, chunkData, 0644); err != nil {
			return fmt.Errorf("保存分片 %d 失败: %v", i, err)
		}
	}

	// 保存完整文件路径（用于流式读取）
	upload.FilePath = requestDir

	// 清空内存中的Body（节省内存）
	upload.Body = nil

	log.Printf("✅ [磁盘存储] 分片完成: %d 个分片已保存到 %s", upload.TotalChunks, requestDir)

	return nil
}

// Get 获取缓存的上传（自动加载大文件）
func (m *UploadCacheManager) Get(requestID string) (*CachedUpload, error) {
	value, ok := m.cache.Load(requestID)
	if !ok {
		return nil, fmt.Errorf("缓存不存在")
	}

	upload := value.(*CachedUpload)

	// 检查是否过期
	if time.Now().After(upload.ExpiresAt) {
		m.Delete(requestID)
		return nil, fmt.Errorf("缓存已过期")
	}

	// 如果是大文件且Body为空，从磁盘加载
	if upload.IsLargeFile && upload.Body == nil {
		log.Printf("📤 [上传缓存] 从磁盘加载: RequestID=%s", requestID[:8]+"...")
		if err := m.loadFromDisk(upload); err != nil {
			return nil, fmt.Errorf("加载失败: %v", err)
		}
	}

	size := float64(len(upload.Body)) / (1024 * 1024)
	log.Printf("📤 [上传缓存] 获取缓存: RequestID=%s, Size=%.2f MB, 模式=%s",
		requestID[:8]+"...", size,
		map[bool]string{true: "磁盘", false: "内存"}[upload.IsLargeFile])

	return upload, nil
}

// loadFromDisk 从磁盘加载大文件
func (m *UploadCacheManager) loadFromDisk(upload *CachedUpload) error {
	if upload.FilePath == "" {
		return fmt.Errorf("文件路径为空")
	}

	log.Printf("📂 [磁盘加载] 开始组装: 分片数=%d", upload.TotalChunks)

	// 预分配内存
	upload.Body = make([]byte, 0, upload.ContentLength)

	// 按顺序读取分片
	for i := 0; i < upload.TotalChunks; i++ {
		chunkPath := filepath.Join(upload.FilePath, fmt.Sprintf("chunk_%d.dat", i))
		chunkData, err := os.ReadFile(chunkPath)
		if err != nil {
			return fmt.Errorf("读取分片 %d 失败: %v", i, err)
		}
		upload.Body = append(upload.Body, chunkData...)
	}

	log.Printf("✅ [磁盘加载] 组装完成: %.2f MB", float64(len(upload.Body))/(1024*1024))

	return nil
}

// Delete 删除缓存（包括磁盘文件）
func (m *UploadCacheManager) Delete(requestID string) {
	value, ok := m.cache.Load(requestID)
	if ok {
		upload := value.(*CachedUpload)
		// 删除磁盘文件
		if upload.IsLargeFile && upload.FilePath != "" {
			if err := os.RemoveAll(upload.FilePath); err != nil {
				log.Printf("⚠️  [上传缓存] 删除磁盘文件失败: %v", err)
			}
		}
	}

	m.cache.Delete(requestID)
	log.Printf("🗑️  [上传缓存] 删除缓存: RequestID=%s", requestID[:8]+"...")
}

// cleanupLoop 定时清理过期缓存
func (m *UploadCacheManager) cleanupLoop() {
	ticker := time.NewTicker(1 * time.Minute)
	defer ticker.Stop()

	for range ticker.C {
		m.cleanupExpired()
	}
}

// cleanupExpired 清理过期缓存
func (m *UploadCacheManager) cleanupExpired() {
	now := time.Now()
	expiredCount := 0

	m.cache.Range(func(key, value interface{}) bool {
		upload := value.(*CachedUpload)
		if now.After(upload.ExpiresAt) {
			requestID := key.(string)
			m.Delete(requestID) // 使用Delete方法确保删除磁盘文件
			expiredCount++
		}
		return true
	})

	if expiredCount > 0 {
		log.Printf("🗑️  [上传缓存] 清理过期缓存: %d 个", expiredCount)
	}
}

// GetStats 获取缓存统计
func (m *UploadCacheManager) GetStats() (count int, totalSize int64) {
	m.cache.Range(func(key, value interface{}) bool {
		upload := value.(*CachedUpload)
		count++
		if upload.IsLargeFile {
			totalSize += upload.ContentLength
		} else {
			totalSize += int64(len(upload.Body))
		}
		return true
	})
	return
}

// GetChunk 获取指定分片（用于客户端分片拉取）
func (m *UploadCacheManager) GetChunk(requestID string, chunkIndex int) ([]byte, error) {
	value, ok := m.cache.Load(requestID)
	if !ok {
		return nil, fmt.Errorf("缓存不存在")
	}

	upload := value.(*CachedUpload)

	// 检查是否过期
	if time.Now().After(upload.ExpiresAt) {
		m.Delete(requestID)
		return nil, fmt.Errorf("缓存已过期")
	}

	if !upload.IsLargeFile {
		return nil, fmt.Errorf("不是大文件，无法分片获取")
	}

	if chunkIndex < 0 || chunkIndex >= upload.TotalChunks {
		return nil, fmt.Errorf("分片索引无效: %d, 总分片数: %d", chunkIndex, upload.TotalChunks)
	}

	// 从磁盘读取分片
	chunkPath := filepath.Join(upload.FilePath, fmt.Sprintf("chunk_%d.dat", chunkIndex))
	data, err := os.ReadFile(chunkPath)
	if err != nil {
		return nil, fmt.Errorf("读取分片失败: %v", err)
	}

	// 验证哈希
	hash := md5.Sum(data)
	chunkHash := hex.EncodeToString(hash[:])

	log.Printf("📦 [分片获取] RequestID=%s, Chunk=%d/%d, Size=%.2f MB, Hash=%s",
		requestID[:8]+"...", chunkIndex+1, upload.TotalChunks,
		float64(len(data))/(1024*1024), chunkHash[:8])

	return data, nil
}

// StreamToWriter 流式写入到Writer（避免一次性加载到内存）
func (m *UploadCacheManager) StreamToWriter(requestID string, writer io.Writer) error {
	value, ok := m.cache.Load(requestID)
	if !ok {
		return fmt.Errorf("缓存不存在")
	}

	upload := value.(*CachedUpload)

	// 检查是否过期
	if time.Now().After(upload.ExpiresAt) {
		m.Delete(requestID)
		return fmt.Errorf("缓存已过期")
	}

	if !upload.IsLargeFile {
		// 小文件：直接写入
		_, err := writer.Write(upload.Body)
		return err
	}

	// 大文件：流式写入分片
	log.Printf("🌊 [流式传输] 开始: RequestID=%s, 分片数=%d",
		requestID[:8]+"...", upload.TotalChunks)

	for i := 0; i < upload.TotalChunks; i++ {
		chunkPath := filepath.Join(upload.FilePath, fmt.Sprintf("chunk_%d.dat", i))
		chunkData, err := os.ReadFile(chunkPath)
		if err != nil {
			return fmt.Errorf("读取分片 %d 失败: %v", i, err)
		}

		if _, err := writer.Write(chunkData); err != nil {
			return fmt.Errorf("写入分片 %d 失败: %v", i, err)
		}
	}

	log.Printf("✅ [流式传输] 完成: RequestID=%s", requestID[:8]+"...")

	return nil
}
